package com.atguigu.daijia.dispatch.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.dispatch.mapper.OrderJobMapper;
import com.atguigu.daijia.dispatch.service.NewOrderService;
import com.atguigu.daijia.dispatch.xxl.client.XxlJobClient;
import com.atguigu.daijia.map.client.LocationFeignClient;
import com.atguigu.daijia.model.entity.dispatch.OrderJob;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.vo.dispatch.NewOrderTaskVo;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.order.NewOrderDataVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class NewOrderServiceImpl implements NewOrderService {

    @Autowired
    private OrderJobMapper orderJobMapper;

    @Autowired
    private XxlJobClient xxlJobClient;

    @Autowired
    private LocationFeignClient locationFeignClient;

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 添加并开始新订单任务调度
     * 说明：每1分钟执行一次，处理任务的bean为：newOrderTaskHandler
     *
     * @param newOrderTaskVo
     * @return
     */
    @Override
    public Long addAndStartTask(NewOrderTaskVo newOrderTaskVo) {
        // 查询当前任务是否已经开启任务调度
        OrderJob orderJob = orderJobMapper.selectOne(new LambdaQueryWrapper<OrderJob>()
                .eq(OrderJob::getOrderId, newOrderTaskVo.getOrderId()));
        if (orderJob == null) {
            // 新增任务id
            Long jobId = xxlJobClient.addAndStart("newOrderTaskHandler", "",
                    "0 0/1 * * * ?", "新订单任务,订单id：" + newOrderTaskVo.getOrderId());

            //将记录保存到数据库
            orderJob = new OrderJob();
            orderJob.setOrderId(newOrderTaskVo.getOrderId());
            orderJob.setJobId(jobId);
            orderJob.setParameter(JSONObject.toJSONString(newOrderTaskVo));
            orderJobMapper.insert(orderJob);
        }

        return orderJob.getJobId();
    }

    /**
     * 执行任务：搜索附近的代驾司机
     *
     * @param jobId
     */
    @Override
    public Boolean executeTask(long jobId) {
        //1根据jobId查询数据库，当前任务是否已经创建
        //如果没有创建，停止执行
        OrderJob orderJob = orderJobMapper
                .selectOne(new LambdaQueryWrapper<OrderJob>().eq(OrderJob::getJobId, jobId));
        if (orderJob == null) {
            return true;
        }
        //2查询当前订单状态，如果当前订单是接单状态，继续执行，如果当前订单状态不是接单状态，停止任务调度
        Integer orderStatus = orderInfoFeignClient.getOrderStatus(orderJob.getOrderId()).getData();
        NewOrderTaskVo newOrderTaskVo = JSONObject.parseObject(orderJob.getParameter(), NewOrderTaskVo.class);
        if (orderStatus.intValue() != OrderStatus.WAITING_ACCEPT.getStatus().intValue()) {
            xxlJobClient.stopJob(jobId);
            log.info("停止任务调度: {}", JSON.toJSONString(newOrderTaskVo));
            return true;
        }
        //3远程调用：搜索附近满足条件可以接单的司机
        //4远程调用后，会获取到满足条件的可以接单的司机列表
        SearchNearByDriverForm searchNearByDriverForm = new SearchNearByDriverForm();
        searchNearByDriverForm.setLongitude(newOrderTaskVo.getStartPointLongitude());
        searchNearByDriverForm.setLatitude(newOrderTaskVo.getStartPointLatitude());
        searchNearByDriverForm.setMileageDistance(newOrderTaskVo.getExpectDistance());
        List<NearByDriverVo> nearByDriverVoList =
                locationFeignClient.searchNearByDriver(searchNearByDriverForm).getData();
        //5遍历司机集合，得到每个司机，为每个司机创建一个临时队列，存储新订单的信息
        nearByDriverVoList.forEach(nearByDriverVo -> {
            //记录司机id，防止重复推送订单信息
            String repeatKey = RedisConstant.DRIVER_ORDER_REPEAT_LIST + newOrderTaskVo.getOrderId();
            Boolean member = stringRedisTemplate.opsForSet().isMember(repeatKey, nearByDriverVo.getDriverId().toString());
            if (member != null && !member) {
                // 将订单id和司机id插入到set集合中 ——》记录该订单已放入司机临时容器
                stringRedisTemplate.opsForSet().add(repeatKey, nearByDriverVo.getDriverId().toString());
                // 设置过期时间
                stringRedisTemplate.expire(
                        repeatKey,
                        RedisConstant.DRIVER_ORDER_REPEAT_LIST_EXPIRES_TIME,
                        TimeUnit.MINUTES);
                //为每个司机创建一个临时队列 采用redis中的list结构
                NewOrderDataVo newOrderDataVo = new NewOrderDataVo();
                BeanUtils.copyProperties(newOrderTaskVo, newOrderDataVo);
                newOrderDataVo.setDistance(nearByDriverVo.getDistance());
                //将消息保存到司机的临时队列里面，司机接单了会定时轮询到他的临时队列获取订单消息
                String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + nearByDriverVo.getDriverId();
                stringRedisTemplate.opsForList().leftPush(key, JSON.toJSONString(newOrderDataVo));
                //过期时间：1分钟，1分钟未消费，自动过期
                //注：司机端开启接单，前端每5秒（远小于1分钟）拉取1次“司机临时队列”里面的新订单消息
                stringRedisTemplate.expire(key, RedisConstant.DRIVER_ORDER_TEMP_LIST_EXPIRES_TIME, TimeUnit.MINUTES);
                log.info("该新订单信息已放入司机临时队列: {}", JSON.toJSONString(newOrderDataVo));
            }
        });
        return true;
    }

    /**
     * 查询司机新订单数据
     *
     * @param driverId
     * @return
     */
    @Override
    public List<NewOrderDataVo> findNewOrderQueueData(Long driverId) {
        List<NewOrderDataVo> newOrderDataVoList = new ArrayList<>();
        String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId;
        Long size = stringRedisTemplate.opsForList().size(key);
        if (size > 0) {
            for (int i = 0; i < size; i++) {
                String NewOrderDataVoStr = stringRedisTemplate.opsForList().rightPop(key);
                NewOrderDataVo newOrderDataVo = JSONObject.parseObject(NewOrderDataVoStr, NewOrderDataVo.class);
                newOrderDataVoList.add(newOrderDataVo);
            }
        }
        return newOrderDataVoList;
    }

    /**
     * 清空新订单队列数据
     *
     * @param driverId
     * @return
     */
    @Override
    public Boolean clearNewOrderQueueData(Long driverId) {
        String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId;
        //直接删除，司机开启服务后，有新订单会自动创建容器
        return stringRedisTemplate.delete(key);
    }
}
